import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import BaseMachineMaintainProject from '@main/entities/service/admin/base/base-machine-maintain-project.entity';
import BaseMachineMaintainProjectList from '@main/entities/service/admin/base/base-machine-maintain-project-list.entity';
import { In, Repository } from 'typeorm';
import { BaseMaintainProjectService } from '@main/service/modules/admin/base/maintainProject/maintainProject.service';
import BaseMaintainProject from '@main/entities/service/admin/base/base-maintain-project.entity';
import { ApiException } from '@main/common/exceptions/api.exception';
import { generateExcelFn } from '@main/excel';
import { SysOperationLogService } from '../../system/operationLog/operationLog.service';
import { getConfigFn } from '@main/config';
import WipMachineMaintain from '@main/entities/service/admin/wip/wip-machine-maintain.entity';
import { WipMachineMaintainService } from '../../wip/machineMaintain/machineMaintain.service';
import { format } from 'date-fns';

@Injectable()
export class BaseMachineMaintainProjectService implements OnModuleInit, OnModuleDestroy {
  constructor(
    @InjectRepository(BaseMachineMaintainProject)
    private machineMaintainProjectRepository: Repository<BaseMachineMaintainProject>,
    @InjectRepository(BaseMachineMaintainProjectList)
    private machineMaintainProjectListRepository: Repository<BaseMachineMaintainProjectList>,
    @InjectRepository(BaseMaintainProject)
    private maintainProjectRepository: Repository<BaseMaintainProject>,
    @InjectRepository(WipMachineMaintain)
    private machineMaintainRepository: Repository<WipMachineMaintain>,
    private baseMaintainProjectService: BaseMaintainProjectService,
    private operationLogService: SysOperationLogService,
    private wipMachineMaintainService: WipMachineMaintainService
  ) {}

  private interval: NodeJS.Timeout;

  syncHostSqlite;

  onModuleInit() {
    const uData = getConfigFn() || ({} as any);

    let { syncHostSqlite } = uData;

    if (syncHostSqlite) {
      this.scheduleNextRun();
    }

    this.syncHostSqlite = syncHostSqlite;
  }

  onModuleDestroy() {
    clearTimeout(this.interval);
  }

  num = 0;

  private scheduleNextRun() {
    const now = new Date();

    const nextRun = new Date(now.getTime() + 60 * 1000); // 当前时间加1分钟

    nextRun.setSeconds(0);

    const timeUntilNextRun = nextRun.getTime() - now.getTime();

    this.interval = setTimeout(() => {
      this.executeTasks();

      this.scheduleNextRun(); // 重新安排下一次运行
    }, timeUntilNextRun);
  }

  private async executeTasks() {
    const machineMaintainList: any = await this.list();

    this.num = this.num + 1;

    // const today = new Date();

    const now = new Date();

    // if (this.num <= 2000) {
    //   // console.log(this.num);

    //   now.setDate(today.getDate() + this.num); // 设置为第二天

    //   now.setHours(0, 0, 0, 0);

    //   console.log(now);
    // }

    for (let i = 0; i < machineMaintainList.length; i++) {
      const { machineMaintainProjectList, machineCode, machineName, machineModel } =
        machineMaintainList[i];

      if (machineMaintainProjectList && machineMaintainProjectList.length) {
        const periodRateCodeObj = {};

        for (let j = 0; j < machineMaintainProjectList.length; j++) {
          const { periodRate, periodRateCode, machineCode } = machineMaintainProjectList[j];

          if (periodRate && periodRateCode && machineCode) {
            if (!periodRateCodeObj[periodRateCode]) {
              periodRateCodeObj[periodRateCode] = {};
            }

            if (!periodRateCodeObj[periodRateCode][periodRate]) {
              periodRateCodeObj[periodRateCode][periodRate] = [];
            }

            // periodRateCodeObj[periodRateCode]['periodRateName'] = periodRateName;

            periodRateCodeObj[periodRateCode][periodRate].push(machineMaintainProjectList[j]);
          }
        }

        for (const i in periodRateCodeObj) {
          const addMachineMaintainList = [];

          for (const j in periodRateCodeObj[i]) {
            let addList: any = [];

            switch (i) {
              case '10':
                // 日
                addList = await this.executeDailyTask(now, periodRateCodeObj[i][j], j);
                break;
              case '20':
                // 周
                addList = await this.executeWeeklyTask(now, periodRateCodeObj[i][j], j);
                break;
              case '30':
                // 月
                addList = await this.executeMonthlyTask(now, periodRateCodeObj[i][j], j);
                break;
              case '40':
                // 季度
                addList = await this.executeQuarterlyTask(now, periodRateCodeObj[i][j], j);
                break;
              case '50':
                // 年
                addList = await this.executeYearlyTask(now, periodRateCodeObj[i][j], j);
                break;

              default:
                break;
            }

            if (addList && addList.length) {
              // console.log(addList);
              addMachineMaintainList.push(...addList);
            }
          }

          if (addMachineMaintainList && addMachineMaintainList.length) {
            // console.log(i);
            // console.log(addMachineMaintainList);
            this.wipMachineMaintainService.add(
              {
                machineMaintainName: `${machineName}${
                  addMachineMaintainList[0].periodRateName
                }${format(new Date(), 'yyyyMMdd')}`,
                machineCode,
                machineName,
                machineModel,
                periodRateCode: i,
                periodRateName: addMachineMaintainList[0].periodRateName,
                planMaintenanceDate: now.getTime(),
                maintenanceUser: '',
                maintenanceStatus: '',
                remark: null,
                machineMaintainList: addMachineMaintainList,
                // createTime: now,
              },
              {},
              '',
              '',
              this.syncHostSqlite,
              '1'
            );
          }
        }
      }
    }
  }

  private async executeDailyTask(now, machineMaintainProjectList, periodRate) {
    if (now.getHours() === 0 && now.getMinutes() === 0) {
      const list = [];

      for (let i = 0; i < machineMaintainProjectList.length; i++) {
        const { machineCode, periodRateCode } = machineMaintainProjectList[i];

        const obj = await this.machineMaintainRepository.findOne({
          where: {
            machineCode,
            periodRateCode,
            isAutoAdd: '1',
          },
          order: {
            createTime: 'DESC',
          },
        });

        if (obj) {
          const { createTime } = obj; // 上次保养时间

          const millisecondsInPeriodRateDays = periodRate * 24 * 60 * 60 * 1000; // periodRate天的毫秒数

          if (now.getTime() - new Date(createTime).getTime() >= millisecondsInPeriodRateDays) {
            delete machineMaintainProjectList[i]['id'];

            list.push(machineMaintainProjectList[i]);
          }
        } else {
          // console.log(machineMaintainProjectList[i]);
          delete machineMaintainProjectList[i]['id'];

          list.push(machineMaintainProjectList[i]);
        }
      }

      return list;
    }

    return [];

    // console.log('list', list);
  }

  private async executeWeeklyTask(now, machineMaintainProjectList, periodRate) {
    if (now.getDay() === 1) {
      // 周一
      const list = [];

      for (let i = 0; i < machineMaintainProjectList.length; i++) {
        const { machineCode, periodRateCode } = machineMaintainProjectList[i];

        const obj = await this.machineMaintainRepository.findOne({
          where: {
            machineCode,
            periodRateCode,
            isAutoAdd: '1',
          },
          order: {
            createTime: 'DESC',
          },
        });

        delete machineMaintainProjectList[i]['id'];

        if (obj) {
          const { createTime } = obj; // 上次保养时间

          const millisecondsInPeriodRateWeeks = periodRate * 7 * 24 * 60 * 60 * 1000; // periodRate周的毫秒数

          if (now.getTime() - new Date(createTime).getTime() >= millisecondsInPeriodRateWeeks) {
            delete machineMaintainProjectList[i]['id'];

            list.push(machineMaintainProjectList[i]);
          }
        } else {
          delete machineMaintainProjectList[i]['id'];

          list.push(machineMaintainProjectList[i]);
        }
      }

      return list;
    }

    return [];
  }

  private async executeMonthlyTask(now, machineMaintainProjectList, periodRate) {
    if (now.getDate() === 1) {
      const list = [];
      const currentYear = now.getFullYear();
      const currentMonth = now.getMonth();

      for (let i = 0; i < machineMaintainProjectList.length; i++) {
        const { machineCode, periodRateCode } = machineMaintainProjectList[i];
        const obj = await this.machineMaintainRepository.findOne({
          where: {
            machineCode,
            periodRateCode,
            isAutoAdd: '1',
          },
          order: {
            createTime: 'DESC',
          },
        });

        const lastMaintenanceDate = obj ? new Date(obj.createTime) : null;

        if (
          !lastMaintenanceDate ||
          lastMaintenanceDate.getFullYear() < currentYear ||
          (lastMaintenanceDate.getFullYear() === currentYear &&
            lastMaintenanceDate.getMonth() <= currentMonth - periodRate)
        ) {
          delete machineMaintainProjectList[i]['id'];

          list.push(machineMaintainProjectList[i]);
        }
      }

      return list;
    }

    return [];
  }

  private async executeQuarterlyTask(now, machineMaintainProjectList, periodRate) {
    if (now.getDate() === 1 && [1, 4, 7, 10].includes(now.getMonth() + 1)) {
      const list = [];
      const currentYear = now.getFullYear();
      const currentQuarter = Math.ceil((now.getMonth() + 1) / 3);

      for (let i = 0; i < machineMaintainProjectList.length; i++) {
        const { machineCode, periodRateCode } = machineMaintainProjectList[i];
        const obj = await this.machineMaintainRepository.findOne({
          where: {
            machineCode,
            periodRateCode,
            isAutoAdd: '1',
          },
          order: {
            createTime: 'DESC',
          },
        });

        const lastMaintenanceDate = obj ? new Date(obj.createTime) : null;

        // 检查是否需要添加维护任务
        if (
          !lastMaintenanceDate ||
          (lastMaintenanceDate.getFullYear() < currentYear &&
            Math.ceil((lastMaintenanceDate.getMonth() + 1) / 3) + Number(periodRate) ===
              currentQuarter + 4) ||
          (lastMaintenanceDate.getFullYear() === currentYear &&
            Math.ceil((lastMaintenanceDate.getMonth() + 1) / 3) + Number(periodRate)) ===
            currentQuarter
        ) {
          delete machineMaintainProjectList[i]['id'];

          list.push(machineMaintainProjectList[i]);
        }
      }

      return list;
    }

    return [];
  }

  private async executeYearlyTask(now, machineMaintainProjectList, periodRate) {
    if (now.getDate() === 1 && now.getMonth() === 0) {
      const list = [];

      const currentYear = now.getFullYear();

      for (let i = 0; i < machineMaintainProjectList.length; i++) {
        const { machineCode, periodRateCode } = machineMaintainProjectList[i];

        const obj = await this.machineMaintainRepository.findOne({
          where: {
            machineCode,
            periodRateCode,
            isAutoAdd: '1',
          },
          order: {
            createTime: 'DESC',
          },
        });

        const lastMaintenanceDate = obj ? new Date(obj.createTime) : null;

        if (!lastMaintenanceDate || lastMaintenanceDate.getFullYear() <= currentYear - periodRate) {
          delete machineMaintainProjectList[i]['id'];

          list.push(machineMaintainProjectList[i]);
        }
      }

      return list;
    }

    return [];
  }

  /**
   * 列举所有角色：除去超级管理员
   */
  async list() {
    const machineMaintainProjectList = await this.machineMaintainProjectRepository.find();

    await Promise.all(
      machineMaintainProjectList.map(async (item) => {
        const { machineCode } = item;

        const machineMaintainProjectList = await this.machineMaintainProjectListRepository.find({
          where: { machineCode },
        });

        item['machineMaintainProjectList'] = machineMaintainProjectList;
      })
    );

    return machineMaintainProjectList;
  }

  /**
   * 分页加载设备保养项目
   */
  async page(data) {
    const { pageSize, pageNum, entity = {} } = data;

    const { machineCode, ids } = entity;

    // 创建查询构造器
    const queryBuilder = this.machineMaintainProjectRepository
      .createQueryBuilder('base_machine_maintain_project') // 主表别名
      .leftJoinAndSelect(
        'base_machine_maintain_project.machineMaintainProjectList',
        'machineMaintainProjectList'
      ) // 联接子表
      .orderBy('base_machine_maintain_project.updateTime', 'DESC'); // 排序

    // 处理 machineCode 查询条件
    if (machineCode) {
      queryBuilder.andWhere('base_machine_maintain_project.machine_code = :machineCode', {
        machineCode,
      });
    }

    // 处理 ids 查询条件
    if (ids && ids.length) {
      queryBuilder.andWhere('base_machine_maintain_project.id IN (:...ids)', { ids });
    }

    // 分页查询
    queryBuilder.skip((pageNum - 1) * pageSize).take(pageSize);

    // return await this.machineMaintainProjectRepository.findAndCount({
    //   where: {
    //     machineCode: Like(`%${machineCode}%`),
    //   },
    //   order: {
    //     updateTime: 'DESC',
    //   },
    //   take: pageSize,
    //   skip: (pageNum - 1) * pageSize,
    // });
    // 执行查询
    const [list, total] = await queryBuilder.getManyAndCount();

    return [list, total];
  }

  async export(dto, lang) {
    const [list] = await this.page(dto);

    if (!list || !(list as any).length) {
      throw new ApiException(10052);
    }

    const dataList = (list as any).map((item) => ({
      ...item,
      list: item.machineMaintainProjectList || [],
    }));

    return await generateExcelFn(
      {
        sheetName: 'machineMaintainProject',
        mainHeaderList: [
          {
            label: 'machineCode',
          },
          {
            label: 'machineName',
          },
          {
            label: 'machineModel',
          },
        ],
        childHeadersName: 'machineMaintainProjectListTableName',
        childHeaderList: [
          {
            label: 'maintainProjectName',
          },
          {
            label: 'maintainProjectCode',
          },
          {
            label: 'unit',
          },
          {
            label: 'part',
          },
          {
            label: 'periodRateName',
          },
          {
            label: 'periodRate',
          },
          {
            label: 'remark',
          },
        ],
        dataList,
      },
      lang
    );
  }

  /**
   * 处理岗位逻辑，如果是新增岗位，则调用岗位新增方法
   */
  async handleMachineMaintainProject(machineMaintainProjectList, user, mac, ip, syncHostSqlite) {
    for (let i = 0; i < machineMaintainProjectList.length; i++) {
      const { maintainProjectCode, maintainProjectName } = machineMaintainProjectList[i];

      const result = await this.maintainProjectRepository.findOne({
        where: { maintainProjectCode },
      });

      if (!result) {
        await this.baseMaintainProjectService.add(
          {
            maintainProjectName,
            maintainProjectCode,
          },
          user,
          mac,
          ip,
          syncHostSqlite
        );
      }
    }
  }

  /**
   * 增加设备保养项目
   */
  async add(data, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];

    const databaseOperationLogList1 = [];

    const { machineCode, machineName, machineModel, machineMaintainProjectList, remark } = data;

    const result = await this.machineMaintainProjectRepository.findOne({
      where: { machineCode },
    });

    // console.log(result);

    if (result) {
      throw new ApiException(10051);
    }

    await this.handleMachineMaintainProject(
      machineMaintainProjectList,
      user,
      mac,
      ip,
      syncHostSqlite
    );

    const machineMaintainProject = await this.machineMaintainProjectRepository.insert({
      machineCode,
      machineName,
      machineModel,
      remark,
      createName: user.nickName,
      createUserName: user.userName,
      updateName: user.nickName,
      updateUserName: user.userName,
    });

    databaseOperationLogList.push({
      type: 'add',
      dataType: 'obj',
      database: 'machineMaintainProjectRepository',
      checkOnlyKeyList: [],
      data: machineMaintainProject,
      whereObj: null,
      remark: `machineMaintainProjectRepository表新增数据`,
    });

    await this.operationLogService.add(
      {
        type: 'add',
        msg: `设备保养项目管理模块新增设备保养项目信息`,
        databaseOperationLogList,
        remark: `新增设备保养项目${data.machineName}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
    const machineMaintainProjectList1 = await this.machineMaintainProjectListRepository.insert(
      machineMaintainProjectList.map((item) => ({ ...item, machineCode }))
    );
    databaseOperationLogList1.push({
      type: 'add',
      dataType: 'obj',
      database: 'machineMaintainProjectListRepository',
      checkOnlyKeyList: [],
      data: machineMaintainProjectList1,
      whereObj: null,
      remark: `machineMaintainProjectListRepository表新增数据`,
    });

    await this.operationLogService.add(
      {
        type: 'add',
        msg: `设备保养项目管理列表新增设备保养项目信息`,
        databaseOperationLogList1,
        remark: `新增设备保养项目${data.machineName}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 更新设备保养项目
   */
  async update(data, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];
    const databaseOperationLogList1 = [];

    const { id, machineCode, remark, machineMaintainProjectList } = data;

    await this.handleMachineMaintainProject(
      machineMaintainProjectList,
      user,
      mac,
      ip,
      syncHostSqlite
    );

    await this.machineMaintainProjectRepository.update(
      { id },
      {
        remark,
        updateName: user.nickName,
        updateUserName: user.userName,
      }
    );
    databaseOperationLogList.push({
      type: 'update',
      dataType: 'obj',
      database: 'machineMaintainProjectRepository',
      checkOnlyKeyList: [],
      data: {
        remark,
        updateName: user.nickName,
        updateUserName: user.userName,
      },
      whereObj: { id },
      remark: `machineMaintainProjectRepository表更新设备保养项目信息`,
    });
    await this.operationLogService.add(
      {
        type: 'update',
        msg: `设备保养项目管理模块更新设备保养项目信息`,
        databaseOperationLogList,
        remark: `更新设备保养项目${data.machineName}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );

    if (machineMaintainProjectList && machineMaintainProjectList.length) {
      await this.machineMaintainProjectListRepository.delete({
        machineCode,
      });
      databaseOperationLogList1.push({
        type: 'delete',
        dataType: 'array',
        database: 'machineMaintainProjectListRepository',
        checkOnlyKeyList: [],
        data: null,
        whereObj: machineCode,
        remark: `machineMaintainProjectListRepository表删除设备保养项目信息(更新时)`,
      });
      await this.operationLogService.add(
        {
          type: 'delete',
          msg: `设备保养项目管理列表删除设备保养项目信息(更新时)`,
          databaseOperationLogList,
          remark: `(更新时)删除设备保养项目信息`,
          mac,
          ip,
          syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
        },
        user
      );

      const machineMaintainProjectList1 = await this.machineMaintainProjectListRepository.insert(
        machineMaintainProjectList.map((item) => ({ ...item, machineCode }))
      );
      databaseOperationLogList1.push({
        type: 'add',
        dataType: 'obj',
        database: 'machineMaintainProjectListRepository',
        checkOnlyKeyList: [],
        data: machineMaintainProjectList1,
        whereObj: null,
        remark: `machineMaintainProjectListRepository表新增数据(更新时)`,
      });

      await this.operationLogService.add(
        {
          type: 'add',
          msg: `设备保养项目管理列表新增设备保养项目信息(更新时)`,
          databaseOperationLogList1,
          remark: `(更新时)新增设备保养项目${data.machineName}`,
          mac,
          ip,
          syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
        },
        user
      );
    }
  }

  /**
   * 根据Id数组删除设备保养项目
   */
  async delete(idList, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];

    const databaseOperationLogList1 = [];

    const machineMaintainProjectList = await this.machineMaintainProjectRepository.find({
      where: {
        id: In(idList),
      },
    });

    for (let i = 0; i < machineMaintainProjectList.length; i++) {
      const { machineCode } = machineMaintainProjectList[i];
      await this.machineMaintainProjectListRepository.delete({ machineCode });
      databaseOperationLogList1.push({
        type: 'delete',
        dataType: 'array',
        database: 'machineMaintainProjectListRepository',
        checkOnlyKeyList: [],
        data: null,
        whereObj: machineCode,
        remark: `machineMaintainProjectListRepository表删除设备保养项目信息(删除时)`,
      });
      await this.operationLogService.add(
        {
          type: 'delete',
          msg: `设备保养项目管理列表删除设备保养项目信息(删除时)`,
          databaseOperationLogList,
          remark: `(删除时)删除设备保养项目信息`,
          mac,
          ip,
          syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
        },
        user
      );
    }

    await this.machineMaintainProjectRepository.delete(idList);

    databaseOperationLogList.push({
      type: 'delete',
      dataType: 'array',
      database: 'machineMaintainProjectRepository',
      checkOnlyKeyList: [],
      data: null,
      whereObj: idList,
      remark: `machineMaintainProjectRepository表删除设备保养项目信息`,
    });
    await this.operationLogService.add(
      {
        type: 'delete',
        msg: `设备保养项目管理模块删除设备保养项目信息`,
        databaseOperationLogList,
        remark: `删除设备保养项目信息`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 根据id获取设备保养项目
   */
  async info(id) {
    const machineMaintainProject = await this.machineMaintainProjectRepository.findOne({
      where: { id },
    });

    if (machineMaintainProject) {
      const { machineCode } = machineMaintainProject;

      const machineMaintainProjectList = await this.machineMaintainProjectListRepository.find({
        where: { machineCode },
      });

      machineMaintainProject['machineMaintainProjectList'] = machineMaintainProjectList;

      return machineMaintainProject;
    }
  }
}
